Skip to content

feat: add sticky PR drift diff comments, Node 24 runtime, and CI fixes - #25

Merged
aatarasoff merged 1 commit into
mainfrom
feat/pr-drift-diff-comment
Nov 15, 2026
Merged

aatarasoff merged 1 commit into
mainfrom
feat/pr-drift-diff-comment

Conversation

@aatarasoff

Copy link
Copy Markdown
Contributor

Summary

Implements pbufio/pbuf-registry#45 and resolves existing CI/security issues on main:

  • Sticky PR schema drift report (drift_diff, fail_on, against, working_directory, comment_on_pr, github_token):
    • Runs pbuf drift diff --format=markdown --fail-on=<threshold>, writes drift_markdown / drift_exit_code outputs and Step Summary, and creates or updates a single sticky PR comment (<!-- pbuf-drift-diff -->) via GitHub REST API without adding extra npm dependencies.
    • Handles read-only fork GITHUB_TOKEN (403) with a warning rather than failing clean PR builds, and posts the drift report before marking the step failed when breaking changes exceed fail_on.
  • Security & runtime hardening:
    • Upgrades action runtime to node24 and default CLI version to v0.16.0 (plus latest support).
    • Validates version and passes it as a positional shell argument instead of string interpolation; masks pbuf_token and github_token via core.setSecret.
    • Declares @actions/exec explicitly and removes unused @actions/tool-cache.
  • CI repairs:
    • Fixes broken unit test URL assertion and v0.5.0 smoke test 404 in ci.yml.
    • Upgrades actions/checkout@v6, actions/setup-node@v6, actions/upload-artifact@v6, and github/codeql-action/*@v4.

@aatarasoff
aatarasoff merged commit 17bf475 into main Nov 15, 2026
3 checks passed
@aatarasoff
aatarasoff deleted the feat/pr-drift-diff-comment branch November 15, 2026 09:03

Copy link
Copy Markdown
Contributor Author

Code Review: Principal Software Engineer & Security Researcher

Reviewed the changes in src/main.js, action.yml, tests, and CI updates for critical issues across security, correctness, performance, reliability, and architecture. The implementation is generally solid: version inputs are strictly validated and passed as positional arguments (avoiding shell injection), tokens are masked via core.setSecret, fork 403s on comment posts are handled gracefully as warnings, drift reports are posted before failing the step, and Node 24 + explicit @actions/exec are good upgrades.

Findings

  • [Severity: High] — Security & Vulnerabilities

    • Location: run() → install via exec.exec('/bin/bash', ['-lc', 'curl -fsSL ${INSTALL_SCRIPT_URL} | sh ...'])
    • Problem: The action downloads and executes a remote shell script from https://raw.githubusercontent.com/pbufio/pbuf-registry/main/scripts/install.sh without pinning a commit SHA or verifying a checksum. A compromise of the main branch (or a MITM on the raw endpoint) grants arbitrary code execution on every consumer's runner. This is a classic supply-chain risk for GitHub Actions that install CLIs.
    • Fix: Prefer downloading a versioned binary (or the script at a specific commit) and verifying it against a published checksum/SHA256. Example sketch:
      // Prefer direct binary download + checksum verification
      // or pin the install script:
      const INSTALL_SCRIPT_URL = 'https://raw.githubusercontent.com/pbufio/pbuf-registry/<commit-sha>/scripts/install.sh'
      // + curl the script to a temp file, verify hash, then execute
  • [Severity: Medium] — Correctness & Edge Cases / Reliability

    • Location: upsertPullRequestComment() — comment listing loop (for (let page = 1; page <= 5; page += 1))
    • Problem: Only the first 500 comments are scanned for the sticky marker. On very active PRs the existing comment can be missed, leading to duplicate sticky comments. Also, a transient API failure during listing aborts the whole upsert (warning only).
    • Fix: Use the GitHub Search API for comments containing the marker, or continue pagination until the marker is found or the list ends, with a higher cap and better retry/backoff. Example:
      // Prefer: GET /search/issues?q=repo:owner/repo+is:pr+commenter:app/pbuf-setup-action+"<!-- pbuf-drift-diff -->"
      // or paginate fully with Link header parsing and a max of ~20 pages
  • [Severity: Medium] — Reliability & Error Handling

    • Location: runDriftDiff() and install exec calls
    • Problem: No explicit timeout is set on @actions/exec or the fetch calls (beyond the 10s AbortSignal). A hung pbuf process or slow registry can stall the job until the runner timeout.
    • Fix: Pass timeout (ms) to exec.exec options and consider a slightly higher AbortSignal for API calls under load.
  • [Severity: Low] — Architecture & Maintainability

    • Location: runDriftDiff() — against and working_directory inputs
    • Problem: against is interpolated into a CLI argument without format validation. Safe today because arguments are passed as an array (no shell), but a malformed tag could produce confusing CLI errors. working_directory is used as cwd with no existence or path-safety check.
    • Fix: Add a simple regex/allow-list for against (similar to VERSION_RE) and optionally fs.existsSync + realpath check for the working directory before running.

No critical injection, auth bypass, or data-exposure issues were found. Secrets masking, input validation on version/fail_on, and the sticky-comment design (with truncation) are well done. Tests cover the happy path, invalid versions, and comment create/update flows.

Go / No-Go Verdict

Conditional Go. The code is safe to deploy for most users, but the unpinned remote install script is a material supply-chain risk that should be addressed (or explicitly accepted with a documented trust boundary) before wide adoption. The pagination limit is the next practical reliability improvement. Otherwise the feature is cleanly implemented and the CI/runtime upgrades are welcome.


Automated rigorous review focused on critical issues only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant